package com.gollum.jpa.util;

import com.gollum.jpa.common.PredicateBuilder;
import com.gollum.jpa.common.Specifications;
import com.gollum.jpa.common.SuperQueryParams;
import com.gollum.jpa.exception.JpaException;
import com.gollum.jpa.common.SuperQueryCustom;
import com.gollum.jpa.constant.MapQueryParamConstant;
import com.gollum.jpa.enums.QueryRuleEnum;
import com.gollum.jpa.enums.SortEnum;
import com.gollum.jpa.enums.SuperQueryMatch;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.util.CollectionUtils;
import org.springframework.util.TypeUtils;

import javax.persistence.criteria.Predicate;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.*;

import static com.gollum.jpa.enums.QueryRuleEnum.NE;
import static javax.persistence.criteria.Predicate.BooleanOperator.AND;
import static javax.persistence.criteria.Predicate.BooleanOperator.OR;

/**
 * <pre>查询参数封装工具类 </pre>
 *
 * @author Herther
 * @version 1.0.0
 * @createTime 2022年08月20日 21:11:00
 */
public class QueryUtils {

    private static final Logger log = LoggerFactory.getLogger(QueryUtils.class);


    /**
     * 根据实体参数获取
     *
     * @param
     * @return
     * @auther: Herther
     * @since 1.0.0
     * @date: 2022/9/11 23:01
     */
    public static <T> Specification <T> buildSpecificationByEntityParams(Class<?> domainClazz, Map<String,Object> params,String filter){
        List<Field> fields = ReflectionUtils.getAllFieldsWithRoot(domainClazz);
        PredicateBuilder<T> predicate = new PredicateBuilder<>(AND);
        Map<String, String> filterMap = formatFilter(filter);
        for (int var1 = 0; var1 < fields.size(); var1++) {
            Field field = fields.get(var1);
            Class<?> type = field.getType();
            String name = field.getName();
            Object value = params.get(name);
            if (Objects.nonNull(value) && StringUtils.isNotEmpty(value.toString())){
                setRule(predicate,filterMap,name,value,type);
            }
        }
        return predicate.build();
    }


    private static <T> void setRule(PredicateBuilder<T> predicate, Map<String,String> filter, String name,Object value, Class<?> type){
        //如果过滤条件为空的话那就是默认eq
        if (filter.isEmpty()){
            predicate.eq(name, value);
        }
        QueryRuleEnum rule =
                StringUtils.isEmpty(filter.get(name)) ? QueryRuleEnum.getByValue("eq") : QueryRuleEnum.getByValue(filter.get(name).toLowerCase());
        //是否要连接
        switch (rule){
            case GT:
                //大于
                predicate.gt(name, (Comparable<?>) value);
                break;
            case LT:
                //小于
                predicate.lt(name, (Comparable<?>) value);
                break;
            case LE:
                //小于等于
                predicate.le(name, (Comparable<?>) value);
                break;
            case NE:
                //不等于
                predicate.ne(name,value);
                break;
            case LIKE:
                //全匹配
                predicate.like(name,value.toString());
                break;
            case LEFT_LIKE:
                //左匹配
                predicate.leftLike(name,value.toString());
                break;
            case RIGHT_LIKE:
                //右匹配
                predicate.rightLike(name,value.toString());
                break;
            case NOT_LIKE:
                //不匹配
                predicate.notLike(name, (String) value);
                break;
            case IN:
                Object[] split = value.toString().split(",");
                predicate.in(name, Arrays.asList(split));
                break;
            case BETWEEN:
                //默认只取前两个,分割
                List<Object> paramsAr = processBetween(value);
                predicate.between(name,paramsAr.get(0) ,paramsAr.get(1));
                break;
            default:
                predicate.eq(name,value);
                break;
        }
    }


    /**
     * filter格式：username:like,password:eq
     *
     * @param filter 过滤参数
     * @return map
     * @auther: Herther
     * @since 1.0.0
     * @date: 2022/9/12 19:50
     */
    public static Map<String,String> formatFilter(String filter){
        if (StringUtils.isEmpty(filter)){
            return new HashMap<String,String>();
        }
        String[] filters = filter.split(",");
        HashMap<String, String> fitlerMap = new HashMap<>();
        for (int var1 = 0; var1 < filters.length; var1++) {
            String filtersValue = filters[var1];
            //sql 过滤
            SqlInjectionUtil.filterContent(filtersValue);
            //分割
            String[] split = filtersValue.split(":");
            fitlerMap.put(split[0], split[1]);
        }
        return fitlerMap;
    }




    /**
     * 动态构造查询条件
     * @param superQueryCustom 查询参数数据结构
     * @param clazz 查询实体
     * @return Specification
     * @auther: Herther
     * @version 1.0.0
     * @date: 2022/8/20 21:14
     */
    public static <T> Specification<T> buildSpecification(SuperQueryCustom superQueryCustom, Class<?> clazz){
        if (Objects.isNull(superQueryCustom)){
            log.error("不允许传入空的参数对象");
            throw new JpaException("Empty parameter objects are not allowed to be passed in");
        }
        String match = superQueryCustom.getSuperQueryMatch().getValue();
        Predicate.BooleanOperator matchEnum = AND;

        if (StringUtils.isNotBlank(match) && StringUtils.equals(match, SuperQueryMatch.OR.getValue())){
            matchEnum = OR;
        }

        return buildSpecification(superQueryCustom.getSuperQueryParams(),matchEnum,clazz);
    }



    /**
     * 功能描述: 动态构造查询条件
     *
     * @param superQueryMap map对象的参数传递
     * @return
     * @auther: Herther
     * @version 1.0.0
     * @date: 2022/8/20 22:35
     */
    public static <T> Specification<T> buildSpecification(Map<String,Object> superQueryMap, Class<?> clazz){
        if (Objects.isNull(superQueryMap)){
            log.error("不允许传入空的参数对象");
            throw new JpaException("Empty parameter objects are not allowed to be passed in");
        }
        String match = (String) superQueryMap.get(MapQueryParamConstant.SUPER_QUERY_MATCH);
        Predicate.BooleanOperator matchEnum = AND;

        if (StringUtils.isNotBlank(match) && StringUtils.equals(SuperQueryMatch.OR.getValue(), match)){
            matchEnum = OR;
        }

        List<SuperQueryParams> superQueryParams = (List<SuperQueryParams> ) superQueryMap.get(MapQueryParamConstant.SUPER_QUERY_PARAMS);
        if (CollectionUtils.isEmpty(superQueryParams)){
            log.error("不允许传入空的参数对象");
            throw new JpaException("Empty parameter objects are not allowed to be passed in");
        }
        return buildSpecification(superQueryParams,matchEnum,clazz);
    }


    /**
     * 功能描述: 动态构造查询条件
     *
     * @param superQueryParams 查询参数合集
     * @param match 连接的规则 and 还是 or
     * @param clazz 实体字节码
     * @return
     * @auther: Herther
     * @version 1.0.0
     * @date: 2022/8/20 22:35
     */
    public static <T> Specification<T> buildSpecification(List<SuperQueryParams> superQueryParams, Predicate.BooleanOperator match, Class<?> clazz){
        if (CollectionUtils.isEmpty(superQueryParams)){
            log.error("不允许传入空的参数对象");
            throw new JpaException("Empty parameter objects are not allowed to be passed in");
        }
        //处理连接条件
        PredicateBuilder<T> predicateBuilder = null;
        if (Objects.isNull(match)){
            log.warn("Please pass in the query connection condition");
            predicateBuilder  =   new PredicateBuilder<>(AND);
        }else{
            predicateBuilder = new PredicateBuilder<>(match);
        }
        //查询的item
        for (int var1 = 0; var1 < superQueryParams.size(); var1++) {
            //SQL 注入过滤
            SqlInjectionUtil.filterContent(superQueryParams.get(var1).getField());
            //如果当前的规则是空的话则结束当次循环
            if (StrUtils.isEmpty(superQueryParams.get(var1).getRule())){
                continue;
            }
            //设置查询的predicate
            setRuleParameters(predicateBuilder,superQueryParams.get(var1));
        }
        return predicateBuilder.build();
    }


    /**
     * 功能描述: 传入一个对象
     *
     * @param:
     * @return:
     * @auther: Herther
     * @version 1.0.0
     * @date: 2022/7/17 15:03
     */
    private static <T> void setRuleParameters(PredicateBuilder<T> predicate, SuperQueryParams superQueryParams){
        //是否要连接
        Boolean condition  = StringUtils.isNotEmpty(superQueryParams.getField()) &&  Objects.nonNull(superQueryParams.getVal());
        // 连接的条件 拼接
        switch (QueryRuleEnum.getByValue(superQueryParams.getRule())){
            case EQ:
                predicate.eq(condition,superQueryParams.getField(),superQueryParams.getVal());
                break;
            case GT:
                //大于
                predicate.gt(condition ,superQueryParams.getField(), (Comparable) superQueryParams.getVal());
                break;
            case LT:
                //小于
                predicate.lt(condition, superQueryParams.getField(), (Comparable) superQueryParams.getVal());
                break;
            case LE:
                //小于等于
                predicate.le(condition,superQueryParams.getField(),(Comparable) superQueryParams.getVal());
                break;
            case NE:
                //不等于
                predicate.ne(condition,superQueryParams.getField(), superQueryParams.getVal());
                break;
            case LIKE:
                //全匹配
                predicate.like(condition,superQueryParams.getField(),superQueryParams.getVal().toString());
                break;
            case LEFT_LIKE:
                //左匹配
                predicate.leftLike(condition,superQueryParams.getField(),  superQueryParams.getVal().toString());
                break;
            case RIGHT_LIKE:
                //右匹配
                predicate.rightLike(condition,superQueryParams.getField(), superQueryParams.getVal().toString());
                break;
            case NOT_LIKE:
                //不匹配
                predicate.notLike(condition,superQueryParams.getField(), (String) superQueryParams.getVal());
                break;
            case IN:
                try {
                    predicate.in(condition, superQueryParams.getField(),(Collection<Object>)superQueryParams.getVal());
                }catch (ClassCastException e){
                    log.error("无法将[{}]转化为：Collection集合", superQueryParams.getVal());
                    throw new JpaException("无法将 [" + superQueryParams.getVal() + "] 转化为：Collection集合");
                }
                break;
            case BETWEEN:
                //默认只取前两个
                List<Object> paramsAr = processBetween(superQueryParams.getVal());
                predicate.between(condition,superQueryParams.getField(),paramsAr.get(0) ,paramsAr.get(1));
                break;
            default:
                throw new JpaException("The query condition is incorrect");
        }
    }

    /**
     * 功能描述: 处理between 参数
     * todo 后期可能需要优化
     * @param object 前台传的参数
     * @auther: Herther
     * @version 1.0.0
     * @date: 2022/8/20 22:07
     */
    private static List<Object> processBetween(Object object){
        //between 两个值之间只用逗号隔开
        if (object instanceof String){
            String paramsArray = (String) object;
            return Arrays.asList(paramsArray.trim().split(","));
        }
        if (object instanceof String[]){
            return Arrays.asList(object);
        }

        if (object instanceof List){
            return (List)object;
        }
        return new ArrayList<>();
    }

    /**
     * 功能描述: 构建排序
     *
     * @auther: Herther
     * @version 1.0.0
     * @date: 2022/8/20 22:16
     */
    public static Sort buildSort(String... sorts) {
        return orderBy(sorts);
    }

    /**
     * 功能描述: 构建排序规则
     *  id:desc@name:asc@id:desc@name:asc 这样的排序字符串
     *  使用逗号分隔，然后
     *  id:desc,name:asc
     * @param
     * @return
     * @auther: Herther
     * @version 1.0.0
     * @date: 2022/8/13 22:49
     */
    public static Sort orderBy(String... sorts) {
        Sort sort = null;
        if (sorts != null && sorts.length >0 ){
            List<Sort.Order> orders = new ArrayList<>();
            String[] var1 = sorts;
            int var2 = sorts.length;
            for (int var3 = 0;var3 < var2; var3++){
                String sortArr = var1[var3];
                String[] sortStr = sortArr.split(":");
                String prop = sortStr[0].trim();
                Sort.Direction direction = Sort.Direction.ASC;
                if (sortStr.length == 2){
                    String sortRule = sortStr[1].toUpperCase();
                    if (sortRule.equals(SortEnum.DESC.name()) || sortRule.equals(SortEnum.ASC.name())){
                        direction = Sort.Direction.valueOf(sortRule);
                    }else{
                        direction = Sort.Direction.valueOf(SortEnum.DESC.name());
                    }

                }
                Sort.Order order = new Sort.Order(direction, prop);
                orders.add(order);
            }

            sort = Sort.by(orders);

        }

        return sort;
    }


}
